($byview => {
    const {env} = $byview;
    let byGlobalLoadingInstance = null;
    let bySingleInitCache = {};
    const util = $byview.util;
    let byviewUrl = $byview.baseUrl;
    
    //功能拓展
    $byview.initVueApp = async app => {
        await $byview.loadScript([byviewUrl + `lang/new-dayjs/zh-cn.js`, byviewUrl + `lang/element/zh-cn.js`])
        
        app.use(ElementPlus, {
            locale: ElementPlus.lang['zhCn']
        });
        
        //国际化
        // await (async app => {
        //     let langMap = {
        //         zh: {file:'zh-cn', varname:'zhCn'},
        //         tl: {file:'th', varname:'th'},
        //         th: {file:'zh-tw', varname:'zhTw'},
        //     };
        //
        //     let lang = (window.vars && vars.langfix) || 'zh';
        //     let langInfo = langMap[lang] || {file:lang, varname:lang};
        //     let dayjsLocale = `byview/lang/new-dayjs/${langInfo.file}.js`;
        //     let elementLocale = `byview/lang/element/${langInfo.file}.js`;
        //
        //     /**
        //      * @var ElementPlus
        //      */
        //     let [BYI18n] = await $byview.loadScript(['byview/plugin/i18n.plugin.js', elementLocale, dayjsLocale], {once:true});
        //
        //     // ElementPlus.locale(ElementPlus.lang[langInfo.varname]);
        //     app.use(ElementPlus, {
        //         locale: ElementPlus.lang[langInfo.varname]
        //     });
        //
        //     let i18nOption = {
        //         locale: langInfo.varname,
        //         [langInfo.varname]: L,
        //     };
        //
        //     if(lang != 'zh') {
        //         i18nOption.fallbackLocale = 'zh';
        //         i18nOption.zh = {};
        //     }
        //
        //     let i18n = new BYI18n(i18nOption);
        //     $byview._i18nOption = i18nOption;
        //     app.use(i18n);
        // })(app);
        //
        let {globalProperties:global} = app.config;
        let oldAlert = global.$alert;
        let oldConfirm = global.$confirm;
    
        let openSingleInstance = async (component, option) => {
            option = option || {};
            let nocache = $byview.vueDebugParams().noCache;
            let ready = option.ready;
        
            if(!bySingleInitCache[component] || nocache) {
                global.$loading('加载组件...');
                let [comp] = await $byview.loadComponent(component);
                let mounted = comp.mounted;
                comp.mounted = function() {
                    mounted && mounted();
                    global.$closeLoading();
                    ready && ready();
                };
            
                let compApp = Vue.createApp(comp);
                await $byview.initVueApp(compApp);
            
                let div = document.createElement('div');
                bySingleInitCache[component] = compApp.mount(div);
                bySingleInitCache[component].onClose = function() {
                    if(nocache) {
                        setTimeout(_ => document.body.removeChild(div), 400);
                    }
                };
            
                document.body.appendChild(div);
            }
        
            let callback = option.callback;
        
            return await new Promise(ok => {
                option.callback = function(result) {
                    global.$closeLoading();
                    ok(result);
                    callback && callback(result);
                };
            
                bySingleInitCache[component].load(option);
            });
        };
        /**
         *  @typedef IVueComponentInstance
         *  @property {IVueComponentInstance_Ctx} ctx
         */
        /**
         * @typedef IVueComponentInstance_Ctx global
         * @property {IVueComponentInstance_Ctx_$formatNumber} $formatNumber
         * @property {IVueComponentInstance_Ctx_$dayjs} $dayjs
         * @property {IVueComponentInstance_Ctx_$elAlert} $elAlert
         * @property {IVueComponentInstance_Ctx_$alert} $alert
         * @property {IVueComponentInstance_Ctx_$alertWarning} $alertWarning
         * @property {IVueComponentInstance_Ctx_$alertError} $alertError
         * @property {IVueComponentInstance_Ctx_$alertInfo} $alertInfo
         * @property {IVueComponentInstance_Ctx_$alertSuccess} $alertSuccess
         * @property {IVueComponentInstance_Ctx_$msgWarning} $msgWarning
         * @property {IVueComponentInstance_Ctx_$msgError} $msgError
         * @property {IVueComponentInstance_Ctx_$msgInfo} $msgInfo
         * @property {IVueComponentInstance_Ctx_$msgSuccess} $msgSuccess
         * @property {IVueComponentInstance_Ctx_$byConfirm} $byConfirm
         * @property {IVueComponentInstance_Ctx_$byConfirmHtml} $byConfirmHtml
         * @property {IVueComponentInstance_Ctx_$byConfirmHtmlCode} $byConfirmHtmlCode
         * @property {IVueComponentInstance_Ctx_$loading} $loading
         * @property {IVueComponentInstance_Ctx_$closeLoading} $closeLoading
         * @property {IVueComponentInstance_Ctx_$dateFormat} $dateFormat
         * @property {IVueComponentInstance_Ctx_$allow} $allow
         * @property {IVueComponentInstance_Ctx_$appAllow} $appAllow
         * @property {IByviewEnv} $env
         */
        global = Object.assign(global, {
            openSingleInstance,
            /**
             * @callback IVueComponentInstance_Ctx_$formatNumber
             * @param number
             * @returns {string}
             */
            $formatNumber(number) {
                if(typeof number == "undefined" || number === null) {
                    number = 0;
                }
                
                return Number(number).toLocaleString('en-us');
            },
            
            /**
             * @callback IVueComponentInstance_Ctx_$loading
             * @param text
             * @param option
             */
            $loading(text, option) {
                text = text || '请稍后...';
                option = Object.assign({
                    text,
                    background: 'rgba(255,255,255,0.0)',
                    customClass: 'by-global-loading',
                    spinner: 'el-icon-loading',
                    lock: true,
                }, option || {});

                byGlobalLoadingInstance = ElementPlus.ElLoading.service(option);
                
                let spinElements = document.querySelectorAll('.by-global-loading .el-loading-spinner');
                for(let i=0; i<spinElements.length; i++) {
                    let spinElement = spinElements[i];
                    
                    if(!spinElement.querySelector('.close')) {
                        let icon = document.createElement('i');
                        icon.className = 'el-icon-close close';
                        spinElement.appendChild(icon);
    
                        icon.onclick = _ => {
                            global.$closeLoading();
                        };
                    }
                }
            },
            
            /**
             * @callback IVueComponentInstance_Ctx_$closeLoading
             */
            $closeLoading() {
                if(byGlobalLoadingInstance) {
                    byGlobalLoadingInstance.close();
                }
            },
            
            /**
             * @type IByviewEnv
             */
            $env: env,
            /**
             * @type IByviewHelper
             */
            $helper: $byview.helper,
            $util: $byview.util,
    
            async $byOpenDebugConfigDialog() {
                return await openSingleInstance(byviewUrl + 'components/common/single/debug-config-popup.vue', {});
            },
        });
        
        global = Object.assign(global, {
            /**
             * dayjs 挂载
             * @callback IVueComponentInstance_Ctx_$dayjs
             * @param {Number|String} date 日期,可以是时间戳整数(毫秒)也可以是字符串日期
             * @param {String} [format] 格式
             *
             * YY	18	                两位数的年份
             * YYYY	2018	            四位数的年份
             * M	1-12	            月份，从 1 开始
             * MM	01-12	            月份，两位数
             * MMM	Jan-Dec	            缩写的月份名称
             * MMMM	January-December	完整的月份名称
             * D	1-31	            月份里的一天
             * DD	01-31	            月份里的一天，两位数
             * d	0-6	                一周中的一天，星期天是 0
             * dd	Su-Sa	            最简写的星期几
             * ddd	Sun-Sat	            简写的星期几
             * dddd	Sunday-Saturday 	星期几
             * H	0-23	            小时
             * HH	00-23	            小时，两位数
             * h	1-12	            小时, 12 小时制
             * hh	01-12	            小时, 12 小时制, 两位数
             * m	0-59	            分钟
             * mm	00-59	            分钟，两位数
             * s	0-59	            秒
             * ss	00-59	            秒 两位数
             * SSS	000-999	            毫秒 三位数
             * Z	+05:00	            UTC 的偏移量，±HH:mm
             * ZZ	+0500	            UTC 的偏移量，±HHmm
             * A	AM PM
             * a	am pm
             *
             * @returns {String}
             */
            $dayjs(date, format) {
                date = date || new Date();
                format = format || 'YYYY-MM-DD HH:mm:ss';
                return dayjs(date).format(format);
            },
            
            /**
             * @callback IVueComponentInstance_Ctx_$dateFormat
             * @param second
             * @param format
             * @returns {String|string}
             */
            $dateFormat(second, format) {
                if(!second || second == '0') {
                    return '';
                }
                
                second = Number(second) * 1000;
                return global.$dayjs(second, format);
            },
            
            /**
             * @callback IVueComponentInstance_Ctx_$elAlert
             * @param msg
             * @param title
             * @param {Object} [opt]
             * @returns {*}
             */
            $elAlert(msg, title, opt) {
                return oldAlert(msg, title, opt);
            },
            
            /**
             * @callback IVueComponentInstance_Ctx_$alert
             * @param msg
             * @param {Object} [opt]
             */
            $alert(msg, opt) {
                oldAlert(msg, '', Object.assign({
                    showClose: false,
                    confirmButtonText: '确定'
                }, opt||{}));
            },
            
            /**
             * 提示错误信息
             * @callback IVueComponentInstance_Ctx_$alertError
             * @param message
             * @param {Object} [opt]
             */
            $alertError(message, opt) {
                global.$alert(message, Object.assign(opt||{}, {
                    type: 'error'
                }));
            },
            
            /**
             * 提示警告信息
             * @callback IVueComponentInstance_Ctx_$alertWarning
             * @param message
             * @param {Object} [opt]
             */
            $alertWarning(message, opt) {
                global.$alert(message,  Object.assign(opt||{}, {
                    type: 'warning'
                }));
            },
            
            /**
             * 提示信息
             * @callback IVueComponentInstance_Ctx_$alertInfo
             * @param message
             * @param {Object} [opt]
             */
            $alertInfo(message, opt) {
                global.$alert(message,  Object.assign(opt||{}, {
                    type: 'info'
                }));
            },
            
            /**
             * 提示成功信息
             * @callback IVueComponentInstance_Ctx_$alertSuccess
             * @param message
             * @param {Object} [opt]
             */
            $alertSuccess(message, opt) {
                global.$alert(message,  Object.assign(opt||{}, {
                    type: 'success'
                }));
            },
        });
        
        
        //提示信息
        let {ElMessage} = ElementPlus;
        
        global = Object.assign(global, {
            $msg(msg, opt) {
                ElMessage(Object.assign({
                    message: msg,
                    showClose: true
                }, opt||{}))
            },
            
            /**
             * 提示错误信息
             * @callback IVueComponentInstance_Ctx_$msgError
             * @param message
             * @param opt
             */
            $msgError(message, opt) {
                global.$msg(message, {
                    type: 'error'
                });
            },
            
            /**
             * 提示警告信息
             * @callback IVueComponentInstance_Ctx_$msgWarning
             * @param message
             * @param opt
             */
            $msgWarning(message, opt) {
                global.$msg(message, {
                    type: 'warning'
                });
            },
            
            /**
             * 提示信息
             * @callback IVueComponentInstance_Ctx_$msgInfo
             * @param message
             * @param opt
             */
            $msgInfo(message, opt) {
                global.$msg(message, {
                    type: 'info'
                });
            },
            
            /**
             * 提示成功信息
             * @callback IVueComponentInstance_Ctx_$msgSuccess
             * @param message
             * @param opt
             */
            $msgSuccess(message, opt) {
                global.$msg(message, {
                    type: 'success'
                });
            },
            
            /**
             * 确认对话框
             * @callback IVueComponentInstance_Ctx_$byConfirm
             * @param content
             * @param option
             * @returns {Promise<unknown>}
             */
            async $byConfirm(content, option) {
                option = Object.assign({type:'warning'}, option || {});
                option.dangerouslyUseHTMLString = option.dangerouslyUseHTMLString || !!option.html;
    
                let focus = null;
                
                if(option.verifyCode) {
                    if(!option.dangerouslyUseHTMLString) {
                        let elm = document.createElement('div');
                        elm.innerText = content;
                        content = elm.innerHTML;
                    }
                    
                    option.dangerouslyUseHTMLString = true;
                    
                    let inputId = "ByConfirmVerifyCode_input_" + new Date().getTime();
                    let code = String(Math.round(Math.random() * 10000));
                    code = code.length < 4 ? '0'.repeat(4-code.length)+code : code;
                    let inputCode = '';
                    
                    $byview._$byConfirmCodeEvent = function(el) {
                        inputCode = el.value;
                    };
                    
                    let codeHtml = `
                        <div class="by-confirm-verify-code">
                            <input type="text" id="${inputId}" maxlength="4" oninput="$byview._$byConfirmCodeEvent(this)" placeholder="验证码"/>
                            <div class="by-confirm-verify-code-content">${code}</div>
                        </div>
                    `;
    
                    content += codeHtml;
    
                    focus = function() {
                        let el = document.getElementById(inputId);
                        el && el.select();
                    };
                    
                    option.beforeClose = function(action, instance, done) {
                        if('confirm' == action && code !== inputCode) {
                            global.$msgError('验证码错误');
                            focus();
                            return;
                        }
    
                        done();
                    };
                }
                
                return await new Promise(ok => {
                    global.$confirm(content, option.title||'', Object.assign({
                        confirmButtonText:'确定',
                        cancelButtonText: '取消'
                    }, option)).then(_ => ok(true)).catch(_=>ok(false));
                    focus && focus();
                });
            },
    
            /**
             * 确认对话框
             * @callback IVueComponentInstance_Ctx_$byConfirmHtml
             * @param content
             * @param option
             * @returns {Promise<unknown>}
             */
            async $byConfirmHtml(content, option) {
                option = option || {};
                option.dangerouslyUseHTMLString = true;
                return await global.$byConfirm(content, option);
            },
    
            /**
             * 确认对话框
             * @callback IVueComponentInstance_Ctx_$byConfirmHtmlCode
             * @param content
             * @param option
             * @returns {Promise<unknown>}
             */
            async $byConfirmHtmlCode(content, option) {
                option = option || {};
                option.dangerouslyUseHTMLString = true;
                option.verifyCode = true;
                return await global.$byConfirm(content, option);
            }
        });
    };
    
    $byview.beforeMount(async app => {
        let {globalProperties:global} = app.config;
        let _topframeLoading = $byview.topframe.loading;
        let _topframeCloseLoading = $byview.topframe.closeLoading;
        
        await $byview.initVueApp(app);
        let {topframe} = $byview;
        
        topframe.loading = text => {
            global.$loading(text);
        };
        
        topframe.alertError = text => {
            global.$alertError(text);
        };
        
        topframe.closeLoading = _ => {
            global.$closeLoading();
            _topframeCloseLoading();
        };
        
        //加载常用的组件
        let components = await $byview.loadComponent([
            //帮助提示组件
            byviewUrl + 'components/base/help.vue'
        ]);
        
        for(let i in components) {
            let component = components[i];
            if(component && component.name) {
                app.component(component.name, component);
            }
        }
        
    });
})(window.$byview);
